home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / program / swagd_f.zip / FAQ.SWG / 0022_FAQ - Modems.pas < prev    next >
Pascal/Delphi Source File  |  1993-11-26  |  49KB  |  1,272 lines

  1.  
  2. This is a FAQ answer on serial communications using the TTY protocol. It
  3. contains information on the TTY protocol and hardware and software implemen-
  4. tations on IBM PCs which is derived from National Semiconductor data sheets.
  5.  
  6. PART ONE - HARDWARE & SOFTWARE
  7.  
  8.  
  9. Acknowledgements
  10. ================
  11.  
  12.   The following persons have contributed (directly or indirectly :-) to this
  13. summary:
  14.       Madis Kaal <mast@anubis.kbfi.ee ??>     this address is known to be bad
  15.       Steve Poulsen <stevep@ims.com>
  16.       Scott C. Sadow <NS16550A@mycro.UUCP>
  17.       Dan Norstedt <?>
  18.         [Commercial: This line could display YOUR name!]
  19.  
  20.  
  21.  
  22. Introduction
  23. ============
  24.  
  25.   One of the most universal parts of the PC is its serial port. You can
  26. connect a mouse, a modem, a printer, a plotter, another PC, ...
  27.   But its usage (both software and hardware) is one of the best-kept secrets
  28. for most users, besides that it is not difficult to understand how to
  29. connect (not plug-in) devices to it and how to program it.
  30.   Regard this FAQ as a manual of the serial port of your PC for both
  31. hardware and software.
  32.  
  33.  
  34. Historical summary
  35. ------------------
  36.  
  37.   In early days of telecommunications, errand-boys and optical signals (flags,
  38. lights, clouds of smoke) were the only methods of transmitting information
  39. across long distances. With increasing requirements on speed and growing
  40. amount of information, more practical methods were developed. One milestone
  41. was the first wire-bound transmission on May 24th, 1844 ("What hath God
  42. wrought", using the famous Morse-alphabet). Well, technology improved a bit,
  43. and soon there were machines that could be used like typewriters, except that
  44. you typed not only on your own piece of paper but also on somebody elses.
  45. The only thing that has changed on the step from the teletyper to your PC
  46. is speed.
  47.  
  48.  
  49. The TTY (teletyping) protocol
  50. -----------------------------
  51.  
  52.   Definition: A protocol is a clear description of the LOGICAL method of
  53. transmitting information. This does NOT include physical realisation.
  54.  
  55.   The TTYp uses two different states of the line called 'mark' and 'space'.
  56. If no data is transmitted, the line is in the 'space' state. Data looks
  57. like
  58.  
  59.       space  ----------+   +-------+   +---+   +-------
  60.                        |   |       |   |   |   |
  61.       mark             +---+       +---+   +---+
  62.  
  63.                         (1)  --------(2)-------- (3)
  64.  
  65.   (1) start bit   (2) data bits   (3) stop bit(s)
  66.  
  67.   Both transmitter (TX) and receiver (RX) use the same data rate (measured
  68. in baud, which is the reciprocal value of the smallest time interval between
  69. two changes of the line state. TX and RX know about the number of data
  70. bits (probably with a parity bit added), and both know about the size of
  71. the stop step (called the stop bit or the stop bits, depending on the size
  72. of the stop step; normally 1, 1.5 or 2 times the size of a data bit). Data
  73. is transmitted bit-synchroneously and word-asynchroneously, which means that
  74. the size of the bits, the length of the word etc.pp. is clearly defined
  75. but the time between two words is undefined.
  76.   The start bit indicates the beginning of a new data word. It is used to
  77. synchronize transmitter and receiver.
  78.   Data is transmitted LSB to MSB, which means that the least significant
  79. bit (Bit 0) is transmitted first with 4 to 7 bits of data following, re-
  80. sulting in 5 to 8 bits of data. A logical '0' is transmitted by the
  81. 'space' state of the line, a logical '1' by 'mark'.
  82.   A parity bit can be added to the data bits to allow error detection.
  83. There are two (well, actually five) kinds of parity: odd and even (plus
  84. none, mark and space). Odd parity means that the number of 'mark' steps in
  85. the data word (including parity bit) is always odd, so the parity bit is
  86. set accordingly (I don't have to explain 'even' parity, must I?). It is
  87. also possible to set the parity bit to a fixed state or to omit it.
  88.   The stop bit does not indicate the end of the word (as it could be
  89. derived from its name); it rather separates two consecutive words by
  90. putting the line into the 'space' state for a minimum time.
  91.   The protocol is usually described by a sequence of numbers and letters,
  92. e.g. 8n1 means 1 start bit (always), 8 bits of data, no parity bit, 1 stop
  93. bit. 7e2 would indicate 7 bits of data, even parity, 2 stop bits (but I've
  94. never seen this one...). The usual thing is 8n1 or 7e1.
  95.   Early teletypers used the neckbreaking speed of 50 baud (which means
  96. that one step is 20ms), 5 bits of data, no parity and 1.5 stop bits (don't
  97. ask me why!). Your PC is capable of serial transmission at up to 115,200
  98. baud (step size of 8.68 microseconds!). Typical rates are 300 baud, 1200 baud,
  99. 2400 baud and 9600 baud.
  100.  
  101.  
  102. The physical transmission
  103. -------------------------
  104.  
  105.   Teletypers used a closed-loop line with a space current of 20ma and a
  106. mark current of 0ma (typical), which allowed to detect a 'broken line'.
  107. The RS232C port of your PC uses voltages rather than currents to indicate
  108. logical states: 'space' is signaled by +3v to +15v (typically +12v), 'mark'
  109. by -3v to -15v (typically -12V). The typical output impedance of the serial
  110. port of a PC is 2 kiloohms (resulting in about 5ma @ 10v), the typical input
  111. impedance is about 4.3 kiloohms, so there should be a maximum fan-out of 5
  112. (5 inputs can be connected to 1 output). Please don't rely on this, it may
  113. differ from PC to PC.
  114.   Three lines (RX, TX & ground) are needed.
  115.  
  116. Q. Why does my PC have a 25pin/9pin connector if there are only 3 lines
  117.    needed?
  118. A. There are several status lines that are only used with a modem. See the
  119.    software section of this FAQ.
  120.  
  121. Q. How can I easily connect two PCs by a three-wire lead?
  122. A. This connection is called a 'null-modem' connection. RX1 is connected
  123.    to TX2 and vice versa, GND1 to GND2. In addition to this, connect RTS
  124.    to CTS & DCD and DTR to DSR (modem software often relies on that). See
  125.    the hardware section for further details.
  126.  
  127.  
  128. Hardware
  129. ========
  130.  
  131.  
  132. The connectors
  133. --------------
  134.  
  135.   PCs have 9pin/25pin male SUB-D connectors. The pin layout is as follows
  136. (looking at the back side of your PC):
  137.  
  138.         1                         13             1               5
  139.       _______________________________      _______________
  140.       \  . . . . . . . . . . . . .  /      \  . . . . .  /
  141.        \  . . . . . . . . . . . .  /            \  . . . .  /
  142.         ---------------------------          -----------
  143.         14                      25            6       9
  144.  
  145.  Name (V24)  25pin  9pin  Dir  Full name               Remarks
  146. --------------------------------------------------------------------------
  147.     TxD         2     2    o   Transmit Data
  148.     RxD         3     3    i   Receive Data
  149.     RTS         4     5    o   Request To Send
  150.     CTS         5     8    i   Clear To Send
  151.     DTR        20     4    o   Data Terminal Ready
  152.     DSR         6     6    i   Data Set Ready
  153.     RI         22     9    i   Ring Indicator
  154.     DCD         8     1    i   Data Carrier Detect
  155.     GND         7     5    -   Signal ground
  156.      -          1     -    -   Protective ground       Don't use this one!
  157.     SCTE       24     -    -   Sync. clock trans. end  Several PCs only
  158.     SCT        15     -    o   Sync. clock TX          dito.
  159.     SCR        17     -    i   Sync. clock RX          dito.
  160.  
  161.   The most important lines are RxD, TxD, and GND. Others are used with
  162. modems, printers and plotters to indicate internal states or to use
  163. synchroneous transmission (this is rarely used, and most PCs don't support
  164. it).
  165.   '0' means +3v to +15V, '1' means -3v to -15v. '1' is the active state.
  166.  
  167.   The lines are:
  168.  
  169.   RxD, TxD: These lines carry the data.
  170.   RTS, CTS: Are used by the PC and the modem/printer/whatsoever (further
  171.     on referred to as the data set) to start/stop a communication. The PC
  172.     sets RTS to its active state ('1'), and the data set responds with CTS
  173.     '1' (always in this order). If the data set wants to stop/interrupt the
  174.     communication (e.g. buffer overflow), it drops CTS to '0'; the PC uses
  175.     RTS to control the data flow.
  176.   DTR, DSR: Are used to establish a connection at the very beginning, i.e.
  177.     the PC and the data set 'shake hands' first to assure they are both
  178.     present. The PC sets DTR to '1', and the data set answers with DSR
  179.     '1'. Modems often indicate hang-up by resetting DSR to '0'.
  180.   (These six lines plus GND are often referred to as '7 wire'-connection or
  181.   'hand shake'-connection.)
  182.   DCD: The modem uses this line to indicate that it has detected the
  183.     carrier of the modem on the other side of the line.
  184.   RI: The modem uses this line to signal that 'the phone rings' (even if
  185.     there isn't a bell fitted to your modem).
  186.   SCTE, SCT, SCR: forget about these.
  187.   Protective ground: This line is connected to the power ground of the
  188.     serial adapter. It should not be used as a signal ground, and it
  189.     MUST NOT be connected to GND (even if your DMM shows up a
  190.     connection!). Connect this line to the screen of the lead (if there is
  191.     one).
  192.  
  193.   Technical data (typical):
  194.  
  195.     Signal level: -10.5v/+11v
  196.     Short circuit current: 6.8ma  (yes, that's enough for your mouse!)
  197.     Output impedance: 2 kiloohms
  198.     Input impedance: 4.3 kiloohms
  199.  
  200.  
  201. Connecting devices
  202. ------------------
  203.  
  204.   Normally, a 7 wire connection is used. Connect:
  205.         GND1    to    GND2
  206.         RxD1    to    TxD2
  207.         TxD1    to    RxD2
  208.         DTR1    to    DSR2
  209.         DSR1    to    DTR2
  210.         RTS1    to    CTS2
  211.         CTS1    to    RTS2
  212.   If a modem is connected, add lines for the following:
  213.         RI, DCD
  214.   If software wants it, connect DCD1 to CTS1 and DCD2 to CTS2.
  215.   BEWARE! While PCs use pin 2 for RxD and pin 3 for TxD, modems normally
  216. have those pins reversed! This allows to easily connect pin1 to pin1, pin2
  217. to pin 2 etc. If you connect two PCs, cross RxD and TxD.
  218.  
  219.   If hardware handshaking is not needed, a so-called null-modem connection
  220. can be used. Connect:
  221.         GND1    to    GND2
  222.         RxD1    to    TxD2
  223.         TxD1    to    RxD2
  224. Additionally, connect (if software needs it):
  225.         RTS1    to    CTS1 & DCD1
  226.         RTS2    to    CTS2 & DCD2
  227.         DTR1    to    DSR1
  228.         DTR2    to    DSR2
  229. You won't need long wires for these!
  230.   The null-modem connection is used to establish an XON/XOFF-transmission
  231. between two PCs (see software section for details).
  232.   Remember: the names DTR, DSR, CTS & RTS refer to the lines as seen from
  233. the PC. This means that for your data set DTR & RTS are incoming signals
  234. and DSR & CTS are outputs!
  235.  
  236.  
  237. Base addresses & interrupts
  238. ---------------------------
  239.  
  240.   Normally, the following list is correct:
  241.  
  242.  
  243.     Port     Base address    Int #
  244.  
  245.     COM1         0x3F8        0xC
  246.     COM2         0x2F8        0xB
  247.     COM3         0x3E8        0xC
  248.     COM4         0x2E8        0xB
  249.  
  250.  
  251.   In PCs, serial communication is realized with a set of three chips
  252. (there are no further components needed!): a UART (Universal Asynchroneous
  253. Receiver/Transmitter) and two line drivers. Normally, the 82450/16450/8250
  254. does the 'brain work' while the 1488 and 1489 drive the lines.
  255.   The chips are produced by many manufacturers; it's of no importance
  256. which letters are printed in front of the numbers (mostly NS for National
  257. Semiconductor). Don't regard the letters behind the number also; they just
  258. indicate special features and packaging (Advanced, FIFO, New, MILitary,
  259. bug fixes [see below] etc.).
  260.   You might have heard of the possibility to replace the 16450 by a 16550A
  261. to improve reliability and software throughput. This is only useful if your
  262. software is able to use the FIFO (first in-first out) buffer features. The
  263. chips are fully pin-compatible except for two pins that are not used by
  264. any board known to the author: pin 24 (CSOUT, chip select out) and pin 29
  265. (NC, no connection to be made). With the 16550A, pin 24 is -TXRDY and pin
  266. 29 is -RXRDY, signals that aren't needed and that even won't care if they
  267. are shorted to +5v or ground. Therefore it should always be possible to
  268. simply replace the 16450 by the 16550A - even if it's not always useful due
  269. to lacking software capabilities. IT IS DEFINITELY NOT NECESSARY FOR
  270. COMMUNICATION UP TO LOUSY 9600 BAUD! These rates can easily be handled by
  271. any CPU and the interrupt-driven communication won't slow down the computer
  272. substantially. But if you want to use high-speed transfer with or without
  273. using the interrupt features (i.e. by 'polling'), it is recommended to use
  274. the 16550A in order to make transmission more reliable if your software
  275. supports it (see excursion some pages below).
  276.  
  277.  
  278. How to detect which chip is used
  279. --------------------------------
  280.  
  281.   This is really not difficult. The 8250 has no scratch register (see data
  282. sheet info below), the 16450/82450 has no FIFO, the 16550 has no working
  283. FIFO :-) and the 16550A performs alright. See the software section for
  284. an example.
  285.  
  286.  
  287. Data sheet information
  288. ----------------------
  289.  
  290.   Some hardware information taken from the data sheet of National
  291. Semiconductor (shortened and commented):
  292.  
  293.   Pin description of the 16450(16550A) [Dual-In-Line package]:
  294.  
  295.                    +-----+ +-----+
  296.                D0 -|  1  +-+   40|- VCC
  297.                D1 -|  2        39|- -RI
  298.                D2 -|  3        38|- -DCD
  299.                D3 -|  4        37|- -DSR
  300.                D4 -|  5        36|- -CTS
  301.                D5 -|  6        35|- MR
  302.                D6 -|  7        34|- -OUT1
  303.                D7 -|  8        33|- -DTR
  304.              RCLK -|  9        32|- -RTS
  305.               SIN -| 10        31|- -OUT2
  306.              SOUT -| 11        30|- INTR
  307.               CS0 -| 12        29|- NC (-RXRDY)
  308.               CS1 -| 13        28|- A0
  309.              -CS2 -| 14        27|- A1
  310.          -BAUDOUT -| 15        26|- A2
  311.               XIN -| 16        25|- -ADS
  312.              XOUT -| 17        24|- CSOUT (-TXRDY)
  313.               -WR -| 18        23|- DDIS
  314.                WR -| 19        22|- RD
  315.               VSS -| 20        21|- -RD
  316.                    +-------------+
  317.  
  318. A0, A1, A2, Register Select, Pins 26-28:
  319. Address signals connected to these 3 inputs select a UART register for
  320. the CPU to read from or to write to during data transfer. A table of
  321. registers and their addresses is shown below. Note that the state of the
  322. Divisor Latch Access Bit (DLAB), which is the most significant bit of the
  323. Line Control Register, affects the selection of certain UART registers.
  324. The DLAB must be set high by the system software to access the Baud
  325. Generator Divisor Latches.
  326.  
  327.   DLAB  A2  A1  A0    Register
  328.     0    0   0   0    Receive Buffer (read) Transmitter Holding Reg. (write)
  329.     0    0   0   1    Interrupt Enable
  330.     x    0   1   0    Interrupt Identification (read)
  331.     x    0   1   0    FIFO Control (write) (undefined on the 16450. CB)
  332.     x    0   1   1    Line Control
  333.     x    1   0   0    Modem Control
  334.     x    1   0   1    Line Status
  335.     x    1   1   0    Modem Status
  336.     x    1   1   1    Scratch (special use on some boards. CB)
  337.     1    0   0   0    Divisor Latch (LSB)
  338.     1    0   0   1    Divisor Latch (MSB)
  339.  
  340. -ADS, Address Strobe, Pin 25: The positive edge of an active Address
  341. Strobe (-ADS) signal latches the Register Select (A0, A1, A2) and Chip
  342. Select (CS0, CS1, -CS2) signals.
  343. Note: An active -ADS input is required when Register Select and Chip
  344. Select signals are not stable for the duration of a read or write
  345. operation. If not required, tie the -ADS input permanently low. (As it is
  346. done in your PC. CB)
  347.  
  348. -BAUDOUT, Baud Out, Pin 15: This is the 16 x clock signal from the
  349. transmitter section of the UART. The clock rate is equal to the main
  350. reference oscillator frequency divided by the specified divisor in the
  351. Baud Generator Divisor Latches. The -BAUDOUT may also be used for the
  352. receiver section by tying this output to the RCLK input of the chip. (Yep,
  353. that's true for your PC. CB).
  354.  
  355. CS0, CS1, -CS2, Chip Select, Pins 12-14: When CS0 and CS1 are high and CS2
  356. is low, the chip is selected. This enables communication between the UART
  357. and the CPU.
  358.  
  359. -CTS, Clear To Send, Pin 36: When low, this indicates that the modem or
  360. data set is ready to exchange data. This signal can be tested by reading
  361. bit 4 of the MSR. Bit 4 is the complement of this signal, and Bit 0 is '1'
  362. if -CTS has changed state since the previous reading (bit0=1 generates an
  363. interrupt if the modem status interrupt has been enabled).
  364.  
  365. D0-D7, Data Bus, Pins 1-8: Connected to the data bus of the CPU.
  366.  
  367. -DCD, Data Carrier Detect, Pin 38: blah blah blah, can be tested by
  368. reading bit 7 / bit 3 of the MSR. Same text as -CTS.
  369.  
  370. DDIS, Driver Disable, Pin 23: This goes low whenever the CPU is reading
  371. data from the UART.
  372.  
  373. -DSR, Data Set Ready, Pin 37: blah, blah, blah, bit 5 / bit 1 of MSR.
  374.  
  375. -DTR, Data Terminal Ready, Pin 33: can be set active low by programming
  376. bit 0 of the MCR '1'. Loop mode operation holds this signal in its
  377. inactive state.
  378.  
  379. INTR, Interrupt, Pin 30: goes high when an interrupt is requested by the
  380. UART. Reset low by the MR.
  381.  
  382. MR, Master Reset, Pin 35: Schmitt Trigger input, resets internal registers
  383. to their initial values (see below).
  384.  
  385. -OUT1, Out 1, Pin 34: user-designated output, can be set low by
  386. programming bit 2 of the MCR '1' and vice versa. Loop mode operation holds
  387. this signal inactive high.
  388.  
  389. -OUT2, Out 2, Pin 31: blah blah blah, bit 3. (Used in your PC to connect
  390. the UART to the interrupt line of the slot when '1'. CB)
  391.  
  392. RCLK, Receiver Clock, Pin 9: This input is the 16 x baud rate clock for
  393. the receiver section of the chip. (Normally connected to -BAUDOUT, as in
  394. your PC. CB)
  395.  
  396. RD, -RD, Read, Pins 22 and 21: When Rd is high *or* -RD is low while the
  397. chip is selected, the CPU can read data from the UART. (One of these is
  398. normally tied. CB)
  399.  
  400. -RI, Ring Indicator, Pin 39: blah blah blah, Bit 6 / Bit 2 of the MSR.
  401. (Bit 2 indicates only change from active low to inactive high! Curious,
  402. isn't it? CB)
  403.  
  404. -RTS, Request To Send, Pin 32: blah blah blah, see DTR (Bit 1).
  405.  
  406. SIN, Serial Input, Pin 10.
  407.  
  408. SOUT, Serial Output, Pin 11: ... Set to 'space' (high) upon MR.
  409.  
  410. -RXRDY, -TYRDY: refer to NS data sheet. Those pins are used for DMA
  411. channeling. Since they are not connected in your PC, I won't describe them
  412. here.
  413.  
  414. VCC, Pin 40, +5v
  415.  
  416. VSS, Pin 20, GND
  417.  
  418. WR, -WR: same as Rd, -RD for writing data.
  419.  
  420. XIN, XOUT, Pins 16 and 17: Connect a crystal here (1.5k betw. xtal & pin 17)
  421. and pin 16 with a capacitor of approx. 20p to GND and other xtal conn. 40p
  422. to GND. Resistor of approx. 1meg parallel to xtal. Or use pin 16 as an input
  423. and pin 17 as an output for an external clock signal.
  424.  
  425.  
  426. Absolute Maximum Ratings:
  427.  
  428.   Temperature under bias: 0 C to +70 C
  429.   Storage Temperature: -65 C to 150 C
  430.   All input or output voltages with respect to VSS: -0.5v to +7.0v
  431.   Power dissipation: 1W
  432.  
  433. Further electrical characteristics see the very good data sheet of NS.
  434.  
  435. UART Reset Configuration
  436.  
  437. Register/Signal        Reset Control      Reset State
  438. --------------------------------------------------------------------
  439.   IER                       MR            0000 0000
  440.   IIR                       MR            0000 0001
  441.   FCR                       MR            0000 0000
  442.   LCR                       MR            0000 0000
  443.   MCR                       MR            0000 0000
  444.   LSR                       MR            0110 0000
  445.   MSR                       MR            xxxx 0000 (according to signals)
  446.   SOUT                      MR            high
  447.   INTR (RCVR errs)     Read LSR/MR        low
  448.   INTR (data ready)    Read RBR/MR        low
  449.   INTR (THRE)          Rd IIR/Wr THR/MR   low
  450.   INTR (modem status)  Read MSR/MR        low
  451.   -OUT2                     MR            high
  452.   -RTS                      MR            high
  453.   -DTR                      MR            high
  454.   -OUt1                     MR            high
  455.   RCVR FIFO           MR/FCR1&FCR0/DFCR0  all bits low
  456.   XMIT FIFO           MR/FCR1&FCR0/DFCR0  all bits low
  457.  
  458.  
  459.  
  460. Known problems with several chips
  461. ---------------------------------
  462.  
  463. (From material Madis Kaal received from Dan Norstedt)
  464.  
  465.     8250 and 8250-B:
  466.  
  467.         * These UARTs pulse the INT line after each interrupt cause has
  468.           been serviced (which none of the others do). [Generates interrupt
  469.           overhead. CB]
  470.  
  471.         * The start bit is about 1 us longer than it ought to be. [This
  472.           shouldn't be a problem. CB]
  473.  
  474.         * 5 data bits and 1.5 stop bits doesn't work.
  475.  
  476.         * When a 1 bit is written to the bit 1 (Tx int enab) in the IER,
  477.           a Tx interrupt is generated. This is an erroneous interrupt
  478.           if the THRE bit is not set. [So don't set this bit as long as
  479.           the THRE bit isn't set. CB]
  480.  
  481.         * The first valid Tx interrupt after the Tx interrupt is enabled
  482.           is probably missed. Suggested workaround:
  483.           1) Wait for the TRHE bit to become set.
  484.           2) Disable CPU interrupts.
  485.           3) Write Tx interrupt enable to the IER.
  486.           4) Write Tx interrupt enable to the IER, again.
  487.           5) Enable CPU interrupts.
  488.  
  489.         * The TSRE (bit 6) doesn't work properly.
  490.  
  491.         * If both the Rx and Tx interrupts are enabled, and a Rx interrupt
  492.           occurs, the IIR indication may be lost; Suggested workarounds:
  493.           1) Test THRE bit in the Rx routine, and either set IER bit 1
  494.              or call the Tx routine directly if it is set.
  495.           2) Test the THRE bit instead of using the IIR.
  496.  
  497.         * [If one of these chips vegetates in your PC, go get your solder
  498.           iron heated... CB]
  499.  
  500.     8250A, 82C50A, 16450 and 16C450:
  501.  
  502.         * (Same problem as above:)
  503.           If both the Rx and Tx interrupts are enabled, and a Rx interrupt
  504.           occurs, the IIR indication may be lost; Suggested workarounds:
  505.           1) Test THRE bit in the Rx routine, and either set IER bit 1
  506.              or call the Tx routine directly if it is set.
  507.           2) Test the THRE bit instead of using the IIR.
  508.           3) [Don't enable both interrupts at the same time. I've never
  509.              had any need to do this. CB]
  510.  
  511.     16550 (without the A):
  512.  
  513.         * Rx FIFO bug: Sometimes a FIFO will get extra characters.
  514.           [This seemed to be very embarracing for NS; they've added a
  515.           simple detection method in the 16550A (bit 6 of IIR). CB]
  516.  
  517. No bugs reported in the 16550A (yet?)
  518.  
  519. [Same is true for the 16C552, a two-in-one version of the 16550A. CB]
  520.  
  521.  
  522.  
  523. Software
  524. ========
  525.  
  526.  
  527.   First some information from the data sheet. Then: HOW TO USE IT.
  528.  
  529.  
  530. Register Description
  531. --------------------
  532.  
  533. See "Hardware" for addresses.
  534.  
  535. Register  Bit 0    Bit 1    Bit 2    Bit 3    Bit 4    Bit 5    Bit 6    Bit 7
  536.  
  537. RBR (r/o)  ----------------------- data bits received ------------------------
  538. THR (w/o)  ------------------ data bits to be transmitted --------------------
  539. IER       ERBFI    ETBEI    ELSI     EDSSI      0        0        0       0
  540. IIR (r/o) pending  IID0     IID1     IID2       0        0      FIFO en  FIFOen
  541. FCR (w/o) enable   RFres    XFres    DMAsel     0        0      - RX trigger -
  542. LCR       - word length -   stopbits PAR en   even sel stick par SBR     DLAB
  543. MCR       DTR      RTS      OUT1     OUT2     Loop       0        0       0
  544. LSR       RBF      OE       PE       FE       Break    THRE     TEMT    FIFOerr
  545. MSR       DCTS     DDSR     TERI     DDCD     CTS      DSR      RI      DCD
  546.  
  547. ERBFI:   Enable Receiver Buffer Full Interrupt
  548. ETBEI:   Enable Transmitter Buffer Empty Interrupt
  549. ELSI:    Enable Line Status Interrupt
  550. EDSSI:   Enable Delta Status Signals Interrupt
  551. IID#:    Interrupt IDentification
  552. RFres:   Receiver FIFO reset
  553. XFres:   Transmitter FIFO reset
  554. SBR:     Set BReak
  555. RBF:     Receiver Buffer Full (Data Available)
  556. OE:      Overrun Error
  557. PE:      Parity Error
  558. FE:      Framing Error
  559. THRE:    Transmitter Holding Register Empty (new data can be written to THR)
  560. TEMT:    Transmitter Empty (last word has been sent)
  561. DCTS:    Delta Clear To Send
  562. DDSR:    Delta Data Set Ready
  563. TERI:    Trailing Edge Ring Indicator
  564. DDCD:    Delta Data Carrier Detect
  565.  
  566. LCR (Line Control Register):
  567.  
  568.    Bit 1  Bit 0    word length         Bit 2      Stop bits
  569.      0      0        5 bits              0            1
  570.      0      1        6 bits              1          1.5/2
  571.      1      0        7 bits         (1.5 if word length is 5)
  572.      1      1        8 bits   (1.5 does not work with some chips, see above)
  573.  
  574.    Bit 5  Bit 4  Bit 3     Parity type       Bit 6   SOUT condition
  575.      x      x      0       no parity           0     normal operation
  576.      0      0      1       odd parity          1     force 'mark' (break)
  577.      0      1      1       even parity       Bit 7   DLAB
  578.      1      0      1       mark parity         0     normal registers
  579.      1      1      1       space parity        1     divisor at reg 0, 1
  580.  
  581. Baud Rate Generator:
  582.  
  583.   DLAB must be set. Write word (16 bits) to address 0 of the UART (this is
  584. the base address) to program baud rate as follows:
  585.      xtal frequency in Hz / 16 / rate = divisor
  586.   Your PC uses an xtal frequency of 1.8432 MHz.
  587.   Do *NOT* use 0 as a divisor (your maths teacher told you so)! [It
  588. results in a rate of some 1000 baud. CB]
  589.   An error of up to 5 percent is irrelevant.
  590.   Some values:
  591.  
  592.      Baud rate   Divisor (hex)   Percent Error
  593.          50          900             0.0%
  594.          75          600             0.0%
  595.         110          417             0.026%
  596.         134.5        359             0.058%
  597.         150          300             0.0%
  598.         300          180             0.0%
  599.         600           C0             0.0%
  600.        1200           60             0.0%
  601.        1800           40             0.0%
  602.        2000           3A             0.69%
  603.        2400           30             0.0%
  604.        3600           20             0.0%
  605.        4800           18             0.0%
  606.        7200           10             0.0%
  607.        9600            C             0.0%
  608.       19200            6             0.0%
  609.       38400            3             0.0%
  610.       56000            2             2.86%
  611.      115200            1             0.0%
  612.  
  613.   NS specifies that the 16550A is capable of 256 kbaud if you use a 4 MHz
  614. or an 8 MHz crystal. But a staff member of NS Germany (I know that this
  615. abbreviation is not well-chosen :-( ) told one of my friends on the phone
  616. that it runs correctly at 512 kbaud as well, but I don't know if the
  617. 1488/1489 manage this. This is true for the 16C552, too.
  618.   BTW: Ever tried 1.76 baud? Kindergarten kids write faster.
  619.   Mice typically use 2400 baud, 8n1.
  620.  
  621.  
  622. LSR (Line Status Register):
  623.  
  624.    Bit 0    Data Ready (DR). Reset by reading RBR.
  625.    Bit 1    Overrun Error (OE). Reset by reading LSR. Indicates loss of data.
  626.    Bit 2    Parity Error (PE). Indicates transmission error. Reset by LSR.
  627.    Bit 3    Framing Error (FE). Indicates missing stop bit. Reset by LSR.
  628.    Bit 4    Break Indicator (BI). Set if 'space' for more than 1 word. Reset
  629.             by LSR.
  630.    Bit 5    Transmitter Holding Register Empty (THRE). Indicates that a new
  631.             word can be written to THR. Reset by writing THR.
  632.    Bit 6    Transmitter Empty (TEMT). Indicates that no transmission is
  633.             running. Reset by reading LSR.
  634.    Bit 7    Set if at least 1 word in FIFO has been received with an error.
  635.             Cleared by reading LSR if there is no further error in the FIFO.
  636.  
  637. FCR (FIFO Control Register):
  638.  
  639.    Bit 0:   FIFO enable.
  640.    Bit 1:   Clear receiver FIFO. This bit is self-clearing.
  641.    Bit 2:   Clear transmitter FIFO. This bit is self-clearing.
  642.    Bit 3:   DMA mode (pins -RXRDY and -TXRDY), see sheet
  643.    Bits 6-7:Trigger level of the DR-interrupt.
  644.    Bit 7  Bit 6    Receiver FIFO trigger level
  645.      0      0         01
  646.      0      1         04
  647.      1      0         08
  648.      1      1         14
  649.  
  650.  
  651.    Excursion: why and how to use the FIFOs (by Scott C. Sadow)
  652.    -----------------------------------------------------------
  653.  
  654.    Normally when transmitting or receiving, the UART generates an
  655.    interrupt for every character sent or received. For 2400 baud, typically
  656.    this is 240/second. For 115,200 baud, this means 11,520/second. With FIFOs
  657.    enabled, the number of interrupts is greatly reduced. For transmit
  658.    interrupts, the UART indicates the transmit holding register is not busy
  659.    until the 16 byte FIFO is full. A transmit hold register empty interrupt
  660.    is not generated until the FIFO is empty (last byte is being sent) Thus,
  661.    the number of transmit interrupts is reduced by a factor of 16. For
  662.    115,200 baud, this means only 7,200 interrupts/second. For receive data
  663.    interrupts, the processing is similar to transmit interrupts. The main
  664.    difference is that the number of bytes in the FIFO before generating an
  665.    interrupt can be set. When the trigger level is reached, a recieve data
  666.    interrupt is generated, but any other data received is put in the FIFO.
  667.    The receive data interrupt is not cleared until the number of bytes in the
  668.    FIFO is below the trigger level.
  669.  
  670.    To add 16550A support to existing code, there are 2 requirements.
  671.  
  672.       1) When reading the IIR to determine the interrupt source, only
  673.          use the lower 3 bits.
  674.  
  675.       2) After the existing UART initialization code, try to enable the
  676.          FIFOs by writing to the FCR. (A value of C7 hex will enable FIFO mode,
  677.          clear both FIFOs, and set the receive trigger level at 14 bytes) Next,
  678.          read the IIR. If Bit 6 of the IIR is not set, the UART is not a
  679.          16550A, so write 0 to the FCR to disable FIFO mode.
  680.  
  681.  
  682. IIR (Interrupt Identification Register):
  683.  
  684.    Bit 3  Bit 2  Bit 1  Bit 0    Priority   Source    Description
  685.      0      0      0      1                 none
  686.      0      1      1      0      highest    Status    OE, PE, FE or BI of the
  687.                                                       LSR set. Serviced by
  688.                                                       reading the LSR.
  689.      0      1      0      0      second     Receiver  DR or trigger level rea-
  690.                                                       ched. Serviced by read-
  691.                                                       ing RBR 'til under level
  692.      1      1      0      0      second     FIFO      No Receiver FIFO action
  693.                                                       since 4 words' time
  694.                                                       (neither in nor out) but
  695.                                                       data in RX-FIFO. Serviced
  696.                                                       by reading RBR.
  697.      0      0      1      0      third      Transm.   THRE. Serviced by read-
  698.                                                       ing IIR (if source of
  699.                                                       int only!!) or writing
  700.                                                       to THR.
  701.      0      0      0      0      fourth     Modem     One of the delta flags
  702.                                                       in the MSR set. Serviced
  703.                                                       by reading MSR.
  704.    Bit 6 & 7: 16550A: set if FCR bit 0 set.
  705.               16550:  bit 7 set, bit 6 cleared
  706.               others: clear
  707.    In most software applications bits 3, 6 & 7 should be masked when servicing
  708.    the interrupt since they are not relevant. These bits cause trouble with
  709.    old software relying on that they are cleared...
  710.    NOTE! Even if some of these interrupts are masked, the service routine
  711.    can be confronted with *all* states shown above when the IIR is loop-polled
  712.    until bit 0 is set. Check examples.
  713.  
  714. IER (Interrupt Enable Register):
  715.  
  716.    Bit 0:   If set, DR interrupt is enabled.
  717.    Bit 1:   If set, THRE interrupt is enabled.
  718.    Bit 2:   If set, Status interrupt is enabled.
  719.    Bit 3:   If set, Modem status interrupt is enabled.
  720.  
  721. MCR (Modem Control Register):
  722.  
  723.    Bit 0:   Programs -DTR. If set, -DTR is low and the DTR pin of the port is
  724.             '1'.
  725.    Bit 1:   Programs -RTS.
  726.    Bit 2:   Programs -OUT1. Not used in a PC.
  727.    Bit 3:   Programs -OUT2. If set, interrupts generated by the UART are trans-
  728.             ferred to the ICU (Interrupt Control Unit).
  729.    Bit 4:   '1': local loopback. All outputs disabled.
  730.  
  731. MSR (Modem Status Register):
  732.  
  733.    Bit 0:   Delta CTS. Set if CTS has changed state since last reading.
  734.    Bit 1:   Delta DSR. Set if DSR has changed state since last reading.
  735.    Bit 2:   TERI. Set if -RI has changed from low to high (i.e. RI at port
  736.             has changed from '1' to '0').
  737.    Bit 3:   Delta DCD. Set if DCD has changed state since last reading.
  738.    Bit 4:   CTS. 1 if '1' at port.
  739.    Bit 5:   DSR.
  740.    Bit 6:   RI. If loopback is selected, it is equivalent to OUT1.
  741.    Bit 7:   DCD.
  742.  
  743.  
  744. PART TWO - PROGRAMMING
  745.  
  746.  
  747. Programming
  748. -----------
  749.  
  750.   Now for the clickety-clickety thing. I hope you're a bit keen in
  751. assembler programming (if not, you've got a problem B-). Programming the UART
  752. in high level languages is, of course, possible, but not at very high
  753. rates or interrupt-driven. I give you several routines in assembler (and,
  754. wherever possible, in C) that do the dirty work for you.
  755.  
  756.   First thing to do is detect which chip is used. It shouldn't be difficult
  757. to convert this C function into assembler; I'll omit the assembly version.
  758.  
  759. int detect_UART(unsigned baseaddr)
  760. {
  761.    // this function returns 0 if no UART is installed.
  762.    // 1: 8250, 2: 16450, 3: 16550, 4: 16550A
  763.    int x;
  764.    // first step: see if the LCR is there
  765.    outp(baseaddr+3,0x1b);
  766.    if (inp(baseaddr+3)!=0x1b) return 0;
  767.    outp(baseaddr+3,0x3);
  768.    if (inp(baseaddr+3)!=0x3) return 0;
  769.    // next thing to do is look for the scratch register
  770.    outp(baseaddr+7,0x55);
  771.    if (inp(baseaddr+7)!=0x55) return 1;
  772.    outp(baseaddr+7,0xAA);
  773.    if (inp(baseaddr+7)!=0xAA) return 1;
  774.    // then check if there's a FIFO
  775.    outp(baseaddr+2,0x1);
  776.    x=inp(baseaddr+2);
  777.    if ((x&0x80)==0) return 2;
  778.    if ((x&0x40)==0) return 3;
  779.    return 4;
  780. }
  781.  
  782.   Remember: if it's not a 16550A, don't use the FIFO mode!
  783.  
  784.  
  785.   Now the non-interrupt version of TX and RX.
  786.  
  787.   Let's assume the following constants are set correctly (either by
  788. 'CONSTANT EQU value' or by '#define CONSTANT value'). You can easily use
  789. variables instead, but I wanted to save the extra lines for the ADD
  790. commands necessary then...
  791.  
  792.   UART_BASEADDR   the base address of the UART
  793.   UART_BAUDRATE   the divisor value (e.g. 12 for 9600 baud)
  794.   UART_LCRVAL     the value to be written to the LCR (e.g. 0x1b for 8N1)
  795.   UART_FCRVAL     the value to be written to the FCR. Bit 0, 1 and 2 set,
  796.                   bits 6 & 7 according to trigger level wished (see above).
  797.                   0x87 is a good value.
  798.  
  799.   First thing to do is initializing the UART. This works as follows:
  800.  
  801. init_UART proc near
  802.   push ax  ; we are 'clean guys'
  803.   push dx
  804.   mov  dx,UART_BASEADDR+3  ; LCR
  805.   mov  al,80h  ; set DLAB
  806.   out  dx,al
  807.   mov  dx,UART_BASEADDR    ; divisor
  808.   mov  ax,UART_BAUDRATE
  809.   out  dx,ax
  810.   mov  dx,UART_BASEADDR+3  ; LCR
  811.   mov  al,UART_LCRVAL  ; params
  812.   out  dx,al
  813.   mov  dx,UART_BASEADDR+4  ; MCR
  814.   xor  ax,ax  ; clear loopback
  815.   out  dx,al
  816.   ;***
  817.   pop  dx
  818.   pop  ax
  819.   ret
  820. init_UART endp
  821.  
  822. void init_UART()
  823. {
  824.    outp(UART_BASEADDR+3,0x80);
  825.    outpw(UART_BASEADDR,UART_BAUDRATE);
  826.    outp(UART_BASEADDR+3,UART_LCRVAL);
  827.    outp(UART_BASEADDR+4,0);
  828.    //***
  829. }
  830.  
  831.   If we wanted to use the FIFO functions of the 16550A, we'd have to add
  832. some lines to the routines above (where the ***s are).
  833. In assembler:
  834.   mov  dx,UART_BASEADDR+2  ; FCR
  835.   mov  al,UART_FCRVAL
  836.   out  dx,al
  837. And in C:
  838.    outp(UART_BASEADDR+2,UART_FCRVAL);
  839.  
  840.   Don't forget to disable the FIFO when your program exits! Some other
  841. software may rely on this!
  842.  
  843.   Not very complex so far, isn't it? Well, I told you so at the very
  844. beginning, and we wanted to start easy. Now let's send a character.
  845.  
  846. UART_send proc near
  847.   ; character to be sent in AL
  848.   push dx
  849.   push ax
  850.   mov  dx,UART_BASEADDR+5
  851. us_wait:
  852.   in   al,dx  ; wait until we are allowed to write a byte to the THR
  853.   test al,20h
  854.   jz   us_wait
  855.   pop  ax
  856.   mov  dx,UART_BASEADDR
  857.   out  dx,al  ; then write the byte
  858.   pop  ax
  859.   pop  dx
  860.   ret
  861. UART_send endp
  862.  
  863. void UART_send(char character)
  864. {
  865.    while ((inp(UART_BASEADDR+5)&0x20)!=0) {;}
  866.    outp(UART_BASEADDR,(int)character);
  867. }
  868.  
  869.   This one sends a null-terminated string.
  870.  
  871. UART_send_string proc near
  872.   ; DS:SI contains a pointer to the string to be sent.
  873.   push si
  874.   push ax
  875.   push dx
  876.   cld  ; we want to read the string in its correct order
  877. uss_loop:
  878.   lodsb
  879.   or   al,al  ; last character sent?
  880.   jz   uss_ende
  881.   ;*1*
  882.   mov  dx,UART_BASEADDR+5
  883.   push ax
  884. uss_wait:
  885.   in   al,dx
  886.   test al,20h
  887.   jz   uss_wait
  888.   mov  dx,UART_BASEADDR
  889.   pop  ax
  890.   out  dx,al
  891.   ;*2*
  892.   jmp  uss_loop
  893. uss_ende:
  894.   pop  dx
  895.   pop  ax
  896.   pop  si
  897.   ret
  898. UART_send_string endp
  899.  
  900. void UART_send_string(char *string)
  901. {
  902.    int i;
  903.    for (i=0; string[i]!=0; i++)
  904.       {
  905.       //*1*
  906.       while ((inp(UART_BASEADDR+5)&0x20)!=0) {;}
  907.       outp(UART_BASEADDR,(int)string[i]);
  908.       //*2*
  909.       }
  910. }
  911.  
  912.   Of course, we could have used our already programmed function/procedure
  913. UART_send instead of the piece of code limited by *1* and *2*, but we are
  914. interested in high-speed code.
  915.  
  916.   It shouldn't be a hard nut for you to modify the above function/procedure
  917. so that it sends a block of data rather than a null-terminated string. I'll
  918. omit that here.
  919.  
  920.   Now for reception. We want to program routines that do the following:
  921.   - check if a character received or an error occured
  922.   - read a character if there's one available
  923.  
  924.   Both the C and the assembler routines return 0 (in AX) if there is
  925. neither an error condition nor a character available. If a character is
  926. available, Bit 8 is set and AL or the lower byte of the return value
  927. contains the character. Bit 9 is set if we lost data (overrun), bit 10
  928. signals a parity error, bit 11 signals a framing error, bit 12 shows if
  929. there is a break in the data stream and bit 15 signals if there are any
  930. errors in the FIFO (if we turned it on). The procedure/function is much
  931. smaller than this paragraph:
  932.  
  933. UART_get_char proc near
  934.   push dx
  935.   mov  dx,UART_BASEADDR+5
  936.   in   al,dx
  937.   mov  ah,al
  938.   and  ah,9fh
  939.   test al,1
  940.   jz   ugc_nochar
  941.   mov  dx,UART_BASEADDR
  942.   in   al,dx
  943. ugc_nochar:
  944.   pop  dx
  945.   ret
  946. UART_get_char endp
  947.  
  948. unsigned UART_get_char()
  949. {
  950.    unsigned x;
  951.    x=(inp(UART_BASEADDR+5)<<8)&0x9f;
  952.    if (x&0x100) x|=((unsigned)inp(UART_BASEADDR))&0xff);
  953.    return x;
  954. }
  955.  
  956.   This procedure/function lets us easily keep track of what's happening
  957. with the RxD pin. It does not provide any information on the modem status
  958. lines! We'll program that later on.
  959.  
  960.   If we wanted to show what's happening with the RxD pin, we'd just have to
  961. write a routine like the following (I use a macro in the assembler version
  962. to shorten the source code):
  963.  
  964. DOS_print macro pointer
  965.   ; prints a string in the code segment
  966.   push ax
  967.   push ds
  968.   push cs
  969.   pop  ds
  970.   mov  dx,pointer
  971.   mov  ah,9
  972.   int  21h
  973.   pop  ds
  974.   pop  ax
  975.   endm
  976.  
  977. UART_watch_rxd proc near
  978. uwr_loop:
  979.   ; check if keyboard hit; we want a possibility to break the loop
  980.   mov  ah,1
  981.   int  16h
  982.   jnz  uwr_exit
  983.   call UART_get_char
  984.   or   ax,ax
  985.   jz   uwr_loop
  986.   test ah,1  ; is there a character in AL?
  987.   jz   uwr_nodata
  988.   push ax    ; yes, print it
  989.   mov  dl,al
  990.   mov  ah,2
  991.   int  21h
  992.   pop  ax
  993. uwr_nodata:
  994.   test ah,0eh ; any error at all?
  995.   jz   uwr_loop  ; this speeds up things
  996.   test ah,2  ; overrun error?
  997.   jz   uwr_noover
  998.   DOS_print overrun_text
  999. uwr_noover:
  1000.   test ah,4  ; parity error?
  1001.   jz   uwr_nopar
  1002.   DOS_print parity_text
  1003. uwr_nopar:
  1004.   test ah,8  ; framing error?
  1005.   jz   uwr_loop
  1006.   DOS_print framing_text
  1007.   jmp  uwr_loop
  1008. overrun_text    db "*** Overrun Error ***$"
  1009. parity_text     db "*** Parity Error ***$"
  1010. framing_text    db "*** Framing Error ***$"
  1011. UART_watch_rxd endp
  1012.  
  1013. void UART_watch_rxd()
  1014. {
  1015.    union _useful_
  1016.       {
  1017.       unsigned val;
  1018.       char character;
  1019.       } x;
  1020.    while (!kbhit())
  1021.       {
  1022.       x.val=UART_get_char();
  1023.       if (!x.val) continue;  // nothing? Continue
  1024.       if (x.val&0x100) putc(x.character);  // character? Print it
  1025.       if (!(x.val&0x0e00)) continue;  // any error condidion? No, continue
  1026.       if (x.val&0x200) printf("*** Overrun Error ***");
  1027.       if (x.val&0x400) printf("*** Parity Error ***");
  1028.       if (x.val&0x800) printf("*** Framing Error ***");
  1029.       }
  1030. }
  1031.  
  1032.   If you call these routines from a function/procedure as shown below,
  1033. you've got a small terminal program!
  1034.  
  1035. terminal proc near
  1036. ter_loop:
  1037.   call UART_watch_rxd  ; watch line until a key is pressed
  1038.   xor  ax,ax  ; get that key from the buffer
  1039.   int  16h
  1040.   cmp  al,27  ; is it ESC?
  1041.   jz   ter_end  ; yes, then end this function
  1042.   call UART_send  ; send the character typed if it's not ESC
  1043.   jmp  ter_loop  ; don't forget to check if data comes in
  1044. ter_end:
  1045.   ret
  1046. terminal endp
  1047.  
  1048. void terminal()
  1049. {
  1050.    int key;
  1051.    while (1)
  1052.       {
  1053.       UART_watch_rxd();
  1054.       key=getche();
  1055.       if (key==27) break;
  1056.       UART_send((char)key);
  1057.       }
  1058. }
  1059.  
  1060.   These, of course, should be called from an embedding routine like the
  1061. following (the assembler routines concatenated will assemble as an .EXE
  1062. file. Put the lines 'code segment' and 'assume cs:code,ss:stack' to the
  1063. front).
  1064.  
  1065. main proc near
  1066.   call UART_init
  1067.   call terminal
  1068.   mov  ax,4c00h
  1069.   int  21h
  1070. main endp
  1071. code ends
  1072. stack segment stack 'stack'
  1073.   dw 128 dup (?)
  1074. stack ends
  1075. end main
  1076.  
  1077. void main()
  1078. {
  1079.    UART_init();
  1080.    terminal();
  1081. }
  1082.  
  1083.   Here we are. Now you've got everything you need to program null-modem
  1084. polling UART software.
  1085.   You know the way. Now go and add functions to check if a data set is
  1086. there, then establish a connection. Don't know how? Set DTR, wait for DSR.
  1087. If you want to send, set RTS and wait for CTS before you actually transmit
  1088. data. You don't need to store old values of the MCR: this register is
  1089. readable. Just read in the data, AND/OR the bit required and write the
  1090. byte back.
  1091.  
  1092.  
  1093.   Now for the interrupt-driven version of the program. This is going to be
  1094. a bit voluminous, so I draw the scene and leave the painting to you. If you
  1095. want to implement interrupt-driven routines in a C program use either the
  1096. inline-assembler feature or link the objects together.
  1097.  
  1098.   First thing to do is initialize the UART the same way as shown above.
  1099. But there is some more work to be done before you enable the UART
  1100. interrupt: FIRST SET THE INTERRUPT VECTOR CORRECTLY! Use Function 0x25 of
  1101. the DOS interrupt 0x21. See also the note on known bugs if you've got a
  1102. 8250.
  1103.  
  1104. UART_INT      EQU 0Ch  ; for COM2 / COM4 use 0bh
  1105. UART_ONMASK   EQU 11101111b  ; for COM2 / COM4 use 11110111b
  1106. UART_OFFMASK  EQU 00010000b  ; for COM2 / COM4 use 00001000b
  1107. UART_IERVAL   EQU ?   ; replace ? by any value between 0h and 0fh
  1108.                       ; (dependent on which ints you want)
  1109.                       ; DON'T SET bit 1 yet!
  1110.  
  1111. initialize_UART_interrupt proc near
  1112.   push ds
  1113.   push cs  ; build a pointer in DS:DX
  1114.   pop  ds
  1115.   lea  dx,interrupt_service_routine
  1116.   mov  ax,2500h+UART_INT
  1117.   int  21h
  1118.   pop  ds
  1119.   mov  dx,UART_BASEADDR+4  ; MCR
  1120.   in   al,dx
  1121.   or   al,8  ; set OUT2 bit to enable interrupts
  1122.   out  dx,al
  1123.   mov  dx,UART_BASEADDR+1  ; IER
  1124.   mov  al,UART_IERVAL
  1125.   out  dx,al
  1126.   in   al,21h  ; last thing to do is unmask the int in the ICU
  1127.   and  al,UART_ONMASK
  1128.   out  21h,al
  1129.   sti  ; and free interrupts if they have been disabled
  1130.   ret
  1131. initialize_UART_interrupt endp
  1132.  
  1133.   Now the interrupt service routine. It has to follow several rules:
  1134. first, it MUST NOT change the contents of any register of the CPU! Then it
  1135. has to tell the ICU (did I tell you that this is the interrupt control
  1136. unit?) that the interrupt is being serviced. Next thing is test which part
  1137. of the UART needs service. Let's have a look at the following procedure:
  1138.  
  1139. interupt_service_routine proc far  ; define as near if you want to link .COM
  1140.   ;*1*
  1141.   push ax
  1142.   push cx
  1143.   push dx
  1144.   push bx
  1145.   push sp
  1146.   push bp
  1147.   push si
  1148.   push di
  1149.   ;*2*   replace the part between *1* and *2* by pusha on an 80186+ system
  1150.   push ds
  1151.   push es
  1152.   mov  al,20h    ; remember: first thing to do in interrupt routines is tell
  1153.   out  20h,al    ; the ICU about it. This avoids lock-up
  1154. int_loop:
  1155.   mov  dx,UART_BASEADDR+2  ; IIR
  1156.   xor  ax,ax  ; clear AH; this is the fastest and shortest possibility
  1157.   in   al,dx  ; check IIR info
  1158.   test al,1
  1159.   jnz  int_end
  1160.   and  al,6  ; we're interested in bit 1 & 2 (see data sheet info)
  1161.   mov  si,ax ; this is already an index! Well-devised, huh?
  1162.   call word ptr cs:int_servicetab[si]  ; ensure a near call is used...
  1163.   jmp  int_loop
  1164. int_end:
  1165.   pop  es
  1166.   pop  ds
  1167.   ;*3*
  1168.   pop  di
  1169.   pop  si
  1170.   pop  bp
  1171.   pop  sp
  1172.   pop  bx
  1173.   pop  dx
  1174.   pop  cx
  1175.   pop  ax
  1176.   ;*4*   *3* - *4* can be replaced by popa on an 80186+ based system
  1177.   iret
  1178. interupt_service_routine endp
  1179.  
  1180.   This is the part of the service routine that does the decisions. Now we
  1181. need four different service routines to cover all four interrupt source
  1182. possibilities (EVEN IF WE DIDN'T ENABLE THEM!! Since 'unexpected'
  1183. interrupts can have higher priority than 'expected' ones, they can appear
  1184. if an expected [not masked] interrupt situation shows up).
  1185.  
  1186. int_servicetab    DW int_modem, int_tx, int_rx, int_status
  1187.  
  1188. int_modem proc near
  1189.   mov  dx,UART_BASE+6  ; MSR
  1190.   in   al,dx
  1191.   ; do with the info what you like; probably just ignore it...
  1192.   ; but YOU MUST READ THE MSR or you'll lock up the system!
  1193.   ret
  1194. int_modem endp
  1195.  
  1196. int_tx proc near
  1197.   ; get next byte of data from a buffer or something
  1198.   ; (remember to set the segment registers correctly!)
  1199.   ; and write it to the THR (offset 0)
  1200.   ; if no more data is to be sent, disable the THRE interrupt
  1201.   ; If the FIFO is used, you can write data as long as bit 5
  1202.   ; of the LSR is 0
  1203.  
  1204.   ; end of data to be sent?
  1205.   ; no, jump to end_int_tx
  1206.   mov  dx,UART_BASEADDR+1
  1207.   in   al,dx
  1208.   and  al,00001101b
  1209.   out  dx,al
  1210. end_int_tx:
  1211.   ret
  1212. int_tx endp
  1213.  
  1214. int_rx proc near
  1215.   mov  dx,UART_BASEADDR
  1216.   in   al,dx
  1217.   ; do with the character what you like (best write it to a
  1218.   ; FIFO buffer)
  1219.   ; the following lines speed up FIFO mode operation
  1220.   mov  dx,UART_BASEADDR+5
  1221.   in   al,dx
  1222.   test al,1
  1223.   jnz  int_rx
  1224.   ret
  1225. int_rx endp
  1226.  
  1227. int_status proc near
  1228.   mov  dx,UART_BASEADDR+5
  1229.   in   al,dx
  1230.   ; do what you like. It's just important to read the LSR
  1231.   ret
  1232. int_status endp
  1233.  
  1234.   How is data sent now? Write it to a FIFO buffer that is read by the
  1235. interrupt routine. Then set bit 1 of the IER and check if this has already
  1236. started transmission. If not, you'll have to start it by yourself... THIS
  1237. IS DUE TO THOSE NUTTY GUYS AT BIG BLUE WHO DECIDED TO USE EDGE TRIGGERED
  1238. INTERRUPTS INSTEAD OF PROVIDING ONE SINGLE FLIP FLOP FOR THE 8253/8254!
  1239.   This procedure can be a C function, too. It is not time-critical at all.
  1240.  
  1241.   ; copy data to buffer
  1242.   mov  dx,UART_BASEADDR+1  ; IER
  1243.   in   al,dx
  1244.   or   al,2  ; set bit 1
  1245.   out  dx,al
  1246.   mov  dx,UART_BASEADDR+5  ; LSR
  1247.   in   al,dx
  1248.   test al,40h  ; is there a transmission running?
  1249.   jz   dont_crank  ; yes, so don't mess it up
  1250.   call int_tx  ; no, crank it up
  1251. dont_crank:
  1252.  
  1253.   Well, that's it! Your main program has to take care about the buffers,
  1254. nothing else!
  1255.  
  1256.   One more thing: always remember that at 115,200 baud there is service to
  1257. be done at least every 8 microseconds! On an XT with 4.77 MHz this is
  1258. about 5 assembler commands!! So forget about servicing the serial port at
  1259. this rate in an interrupt-driven manner on such computers. An AT with 12
  1260. MHz probably will manage it if you use 'macro commands' such as pusha and/or
  1261. a 16550A in FIFO mode. An AT can perform about 20 instructions between two
  1262. characters, a 386 with 25 MHz will do about 55, and a 486 with 33 MHz will
  1263. manage about 150. Using a 16550A is strongly recommended at high rates.
  1264.   The interrupt service routines can be accelerated by not pushing that
  1265. much registers, and pusha and popa are fast instructions.
  1266.  
  1267.   Another last thing: due to the poor construction of the PC interrupt
  1268. system, one interrupt line can only be driven by one device. This means if
  1269. you want to use COM3 and your mouse is connected to COM1, you can't use
  1270. interrupt features without disabling the mouse (write 0x0 to the mouse's
  1271. MCR).
  1272.